package TestPaperSystem.service.impl;

import TestPaperSystem.Dto.*;
import TestPaperSystem.config.product.Product;
import TestPaperSystem.enmus.PaperStatus;
import TestPaperSystem.enmus.QuType;
import TestPaperSystem.entity.*;
import TestPaperSystem.mapper.PaperMapper;
import TestPaperSystem.service.*;
import TestPaperSystem.utils.DateGetter;
import TestPaperSystem.utils.ThreadPool;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author xwj
 * @since 2023-03-25
 */
@Service
public class PaperServiceImpl extends ServiceImpl<PaperMapper, Paper> implements PaperService {

    @Resource
    PaperMapper paperMapper;

    @Autowired
    ExamRepoService examRepoService;

    @Autowired
    QuService quService;

    @Autowired
    AnswerService answerService;

    @Autowired
    PaperQuAnswerService paperQuAnswerService;

    @Autowired
    ExamPaperQuService examPaperQuService;

    @Autowired
    UserExamService userExamService;

    @Autowired
    Product product;

    @Autowired
    UserService userService;

    @Autowired
    ExamConfigService examConfigService;

    @Autowired
    ThreadPool threadPool;

    @Autowired
    SimilarityService similarityService;

    private static List<String> select = Arrays.asList(new String[]{"A","B","C","D","E","F","G","H"});

    @Override
    public Page selfPaperList(PaperVo paperVo) throws Exception {
        QueryWrapper<Paper> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(Paper::getExamId, paperVo.getExamId())
                .eq(paperVo.getUserId() != null ,Paper::getUserId , paperVo.getUserId())
                .eq(paperVo.getState() != null, Paper::getState , paperVo.getState())
                .orderByDesc(Paper::getCreateTime);
        int count = paperMapper.selectCount(queryWrapper);
        Page papers = new Page();



        if(paperVo.getUserId() != null){

            papers = paperMapper.selectPage(new Page<Paper>(paperVo.getCurrentPage() , paperVo.getSize()),queryWrapper);
            papers.setTotal(count);
        }
        else{

//            QueryWrapper<Paper> queryWrapper = new QueryWrapper<>();
//            queryWrapper.lambda().eq(Paper::getExamId, paperVo.getExamId())
//                    .eq(paperVo.getExamId() != null ,Paper::getExamId , paperVo.getExamId())
//                    .eq(paperVo.getState() != null, Paper::getState , paperVo.getState())
//                    .orderByDesc(Paper::getCreateTime);
//            int counts = paperMapper.selectCount(queryWrapper);
//            papers.setTotal(counts);
            List<Paper_check_DTO> lists = paperMapper.getPaperCheckDTO((paperVo.getCurrentPage() - 1) * paperVo.getSize()
                    , paperVo.getSize(),paperVo.getExamId(),paperVo.getState());
            papers.setRecords(lists);
            papers.setTotal(count);
        }
        if(papers.getRecords().size() == 0){
            throw new Exception("暂无数据");
        }

        return papers;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String createPaper(PaperVo paper) throws Exception{

        Paper result = paperMapper.selectOne(new QueryWrapper<Paper>().lambda()
                .eq(Paper::getUserId,paper.getUserId())
                .eq(Paper::getState,PaperStatus.ING));
        if(result != null)
            throw new Exception("您有正在进行的考试");

        boolean dif_paper = examConfigService.getOne(new QueryWrapper<ExamConfig>().lambda()
                .eq(ExamConfig::getExamId , paper.getExamId())
                .select(ExamConfig::getDifPaper)).getDifPaper();

        ExamDTO exam = examRepoService.getByExamId(paper.getExamId());
        List<ExamPaperQu> quList = this.createQuByRepo(exam , dif_paper);

        if(exam == null){
            throw new Exception("试卷未找到");
        }

        if(exam.getState() != 0){
            throw new Exception("试卷状态不正确");
        }
        if(quList.isEmpty()){
            throw new Exception("试题未准备好");
        }
        Paper paper1 = this.savePaper(paper.getUserId() , exam , quList ,dif_paper);
        //存入消息队列
        product.delaySendMsg("delay_Queue", paper1.getId() , paper1.getTotalTime() * 60 * 1000L);
        return paper1.getId();
    }

    @Override
    public PaperDetails paperDetails(String paperId) throws Exception {

        PaperDetails paperDetails = new PaperDetails();

        Paper paper = this.getById(paperId);

        if(paper == null)
            throw new Exception("未找到试卷");

        BeanUtils.copyProperties(paper , paperDetails);

        List<ExamPaperQu> radioList = new ArrayList<>();
        List<ExamPaperQu> multiList = new ArrayList<>();
        List<ExamPaperQu> judgeList = new ArrayList<>();
        List<ExamPaperQu> saqList = new ArrayList<>();

        List<ExamPaperQu> quList = examPaperQuService.list(new QueryWrapper<ExamPaperQu>().lambda().eq(ExamPaperQu::getPaperId,paperId));
        for(ExamPaperQu item : quList){
            switch (item.getQuType()){
                case QuType.RADIO:
                    radioList.add(item);
                    break;
                case QuType.MULTI:
                    multiList.add(item);
                    break;
                case QuType.JUDGE:
                    judgeList.add(item);
                    break;
                default:
                    saqList.add(item);
                    break;
            }
        }

        paperDetails.setRadioList(radioList );
        paperDetails.setMultiList(multiList);
        paperDetails.setJudgeList(judgeList );
        paperDetails.setSaqList(saqList );

        return paperDetails;

    }

    @Override
    public PaperQuDTO quDetails(PaperQuVo paperQuVo) throws Exception {

        PaperQuDTO quDTO = new PaperQuDTO();

        ExamPaperQu examPaperQu = examPaperQuService.getOne(new QueryWrapper<ExamPaperQu>().lambda()
                .eq(ExamPaperQu::getPaperId , paperQuVo.getPaperId())
                .eq(ExamPaperQu::getQuId , paperQuVo.getQuId()));


        Qu qu = quService.getById(paperQuVo.getQuId());

        List<PaperQuAnswerDTO> list = paperQuAnswerService.listByExam(paperQuVo.getPaperId() , paperQuVo.getQuId());

        if((examPaperQu == null) || (qu == null) || (list == null && list.size() > 0) )
            throw new Exception("出现错误");

        BeanUtils.copyProperties(examPaperQu , quDTO);
        BeanUtils.copyProperties(qu,quDTO);
        quDTO.setAnswerList(list);

        return quDTO;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void fillAnswer(PaperQuVo paperQuVo) throws Exception {
        if(paperQuVo.getAnswer().equals("") && paperQuVo.getAnswerIds().isEmpty())
            return;
        List<PaperQuAnswer> paperQuAnswers = paperQuAnswerService.list(new QueryWrapper<PaperQuAnswer>().lambda()
                .eq(PaperQuAnswer::getPaperId,paperQuVo.getPaperId())
                .eq(PaperQuAnswer::getQuId,paperQuVo.getQuId()));
        Boolean is_right = true;
        for(PaperQuAnswer item : paperQuAnswers){
            if(!paperQuVo.getAnswerIds().contains(String.valueOf(item.getId()))){
                item.setChecked(false);
            }else{
                item.setChecked(true);
            }

            if( item.getIsRight() != null && !item.getIsRight().equals(item.getChecked()))
                is_right = false;
            paperQuAnswerService.updateById(item);
        }

        ExamPaperQu examPaperQu = new ExamPaperQu();

        examPaperQu.setAnswer(paperQuVo.getAnswer());
        examPaperQu.setPaperId(paperQuVo.getPaperId());
        examPaperQu.setQuId(paperQuVo.getQuId());
        examPaperQu.setAnswered(true);
        examPaperQu.setIsRight(is_right);

        examPaperQuService.updateByKeys(examPaperQu);
    }

    @Override
    public void handExam(PaperQuVo paperQuVo) throws Exception {
        Paper paper = this.getById(paperQuVo.getPaperId());

        if(PaperStatus.ING != paper.getState()){
            throw new Exception("试卷状态不正确");
        }
        int objScore = examPaperQuService.claObjScore(paperQuVo.getPaperId());
        int subjScore = examPaperQuService.claSubjScore(paperQuVo.getPaperId());
        if(paper.getHasSaq()){
            paper.setState(PaperStatus.WAIT_CHECK);
        }else{
            paper.setState(PaperStatus.FINISHED);
        }
        paper.setObjScore(objScore);
        paper.setSubjScore(subjScore);
        paper.setUserScore(objScore + subjScore);

        //提交试卷时进行对比

        threadPool.pool.execute(new Runnable() {
            @Override
            public void run() {
                try{
                    similarityService.getSimilarity(paper);
                }catch (Exception e){
                    e.printStackTrace();
                }

            }
        });
        threadPool.pool.execute(new Runnable() {
            @Override
            public void run() {
                 updateMsg(paper);
            }
        });
//        new Thread(() -> {
//            this.updateMsg(paper);
//        }).start();
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateMsg(Paper paper) {
        UserExam userExam = new UserExam();

        userExam.setUpdateTime(DateGetter.getInstance().getCurrentTime());
        userExam.setExamId(paper.getExamId());
        userExam.setIsDone(true);
        userExam.setUserId(paper.getUserId());
        userExam.setPassed(paper.getUserScore() >= paper.getQualifyScore());
        userExam.setScore(paper.getUserScore());
        this.updateById(paper);
        userExamService.save(userExam);
    }

    @Override
    public String checkProcess(String userId) throws Exception {
        Paper result = paperMapper.selectOne(new QueryWrapper<Paper>().lambda()
                .eq(Paper::getUserId,userId)
                .eq(Paper::getState,PaperStatus.ING));
        return result == null ? null:result.getId();
    }

    @Override
    public PaperResult paperResultById(PaperVo paperVo) throws Exception {

        PaperResult paperResult = new PaperResult();

        List<PaperQuDTO> dtoList = new ArrayList<>();

        Paper paper = this.getById(paperVo.getId());

        if(paper == null)
            throw new Exception("暂无数据");
        BeanUtils.copyProperties(paper , paperResult);
        User user = userService.getById(paper.getUserId());
        BeanUtils.copyProperties(user , paperResult);
        List<ExamPaperQu> lists = examPaperQuService.list(new QueryWrapper<ExamPaperQu>().lambda().eq(ExamPaperQu::getPaperId , paper.getId()));
        for(ExamPaperQu item : lists){

            PaperQuVo paperQuVo = new PaperQuVo();

            paperQuVo.setPaperId(paperVo.getId());

            paperQuVo.setQuId(item.getQuId());

            dtoList.add(this.quDetails(paperQuVo));
        }
        paperResult.setQuList(dtoList);
        return paperResult;
    }

    @Override
    public Boolean updateState(PaperVo paperVo) throws Exception {

        Paper paper = this.getById(paperVo.getId());

        if(paper == null){
            throw new Exception("查找失败");
        }
        if(paper.getState() != PaperStatus.WAIT_CHECK)
            throw new Exception("状态修改失败");
        paper.setState(PaperStatus.FINISHED);
        return this.updateById(paper);
    }


    //保存试卷当前需要填写试卷人的考试信息
    private Paper savePaper(String userId , ExamDTO examDTO , List<ExamPaperQu> quList , boolean dif_paper) {

        Paper paper = new Paper();

        paper.setExamId(examDTO.getId());
        paper.setUserId(userId);
        paper.setTitle(examDTO.getExamTitle());
        paper.setTotalScore(examDTO.getTotalScore());
        paper.setQualifyScore(examDTO.getQualifyScore());
        paper.setHasSaq(true);
        paper.setTotalTime(examDTO.getTotalTime());
        paper.setCreateTime(DateGetter.getInstance().getCurrentTime());
        paper.setUpdateTime(DateGetter.getInstance().getCurrentTime());
        paper.setState(PaperStatus.ING);
        paper.setUserScore(0);
        paper.setUserTime(0);
        paper.setLimitTime(DateGetter.getInstance().getTimeAftTime("MINUTE",System.currentTimeMillis(),examDTO.getTotalTime()));
        paper.setId(String.valueOf(System.currentTimeMillis()));

        this.save(paper);

        this.savePaperQuAndAnswer(paper.getId(),quList ,dif_paper);

        return paper;


    }
    //保存试卷的问题与答案
    private void savePaperQuAndAnswer(String paperId , List<ExamPaperQu> quList ,boolean dif_paper){
        int sort = 0;
        List<PaperQuAnswer> bitchAnswer = new ArrayList<>();

        for(ExamPaperQu item : quList){
            item.setSort(sort);
            item.setPaperId(paperId);

            List<Answer> list = answerService.list(new QueryWrapper<Answer>().lambda().eq(Answer::getQuId , item.getQuId()));
            if(dif_paper){
                list = this.reverseCantor(list);
            }
            int sort_1 = 0;
            for(Answer answer : list){
                PaperQuAnswer paperQuAnswer = new PaperQuAnswer();
                paperQuAnswer.setAbc(select.get(sort_1));
                paperQuAnswer.setAnswerId(answer.getId());
                paperQuAnswer.setIsRight(answer.getIsRight());
                paperQuAnswer.setChecked(false);
                paperQuAnswer.setPaperId(paperId);
                paperQuAnswer.setQuId(answer.getQuId());
                paperQuAnswer.setSort(sort_1);
                sort_1++;
                bitchAnswer.add(paperQuAnswer);
            }

            sort++;
        }
        //添加问题
        examPaperQuService.saveBatch(quList);
        //添加问题答案
        paperQuAnswerService.saveBatch(bitchAnswer);

    }


    //按照题库生成试题 , 千人千卷将开启康拓散列
    private List<ExamPaperQu> createQuByRepo(ExamDTO exam , boolean dif_paper) throws Exception {

        ExamDTO examDTO = examRepoService.getByExamId(exam.getId());

        List<ExamPaperQu> quList = new ArrayList<>();

        //防止题目重复
        List<String> excludes = new ArrayList<>();

        if(!examDTO.getRepoList().isEmpty()){

            for(ExamRepo item : examDTO.getRepoList()){
                //单选
                if(item.getRadioCount() > 0){
                    List<Qu> radioList = quService.getList(item.getRadioCount() , excludes , QuType.RADIO , item.getRepoId());
                    if(radioList != null){
                        if(dif_paper){
                          radioList = this.reverseCantor(radioList);
                        }
                        for(Qu qu : radioList){
                            quList.add(this.fillQu(qu , item));
                            excludes.add(qu.getId());
                        }
                    }

                }
                //多选
                if(item.getMultiCount() > 0){
                    List<Qu> multiList = quService.getList(item.getMultiCount() , excludes , QuType.MULTI , item.getRepoId());
                    if(multiList != null){
                        if(dif_paper){
                            multiList = this.reverseCantor(multiList);
                        }
                        for(Qu qu : multiList ){
                            quList.add(this.fillQu(qu , item));
                            excludes.add(qu.getId());
                        }
                    }

                }

                //判断
                if(item.getJudgeCount() > 0){
                    List<Qu> judgeList = quService.getList(item.getJudgeCount() , excludes , QuType.JUDGE , item.getRepoId());
                    if(judgeList != null){
                        if(dif_paper){
                            judgeList = this.reverseCantor(judgeList);
                        }
                        for(Qu qu : judgeList){
                            quList.add(this.fillQu(qu , item));
                            excludes.add(qu.getId());
                        }
                    }

                }
                //主观 ,题目类型大于等于4
                if(item.getSaqCount() > 0){
                    List<Qu> saqList = quService.getList(item.getSaqCount() , excludes , QuType.PROGRAM , item.getRepoId());
                    if(saqList != null){
                        if(dif_paper){
                            saqList = this.reverseCantor(saqList);
                        }
                        for(Qu qu : saqList){
                            quList.add(this.fillQu(qu , item));
                            excludes.add(qu.getId());
                        }
                    }
                }
            }

        }

        return quList;
    }

    //完善试题信息
    private ExamPaperQu fillQu(Qu qu , ExamRepo examRepo){
        ExamPaperQu examPaperQu = new ExamPaperQu();

        examPaperQu.setAnswered(false);
        examPaperQu.setQuId(qu.getId());
        examPaperQu.setQuType(qu.getQuType());
        examPaperQu.setIsRight(false);
        examPaperQu.setAnswer("");
        switch (qu.getQuType()){
            case QuType.RADIO: //单选
                examPaperQu.setActualScore(examRepo.getRadioScore());
                examPaperQu.setScore(examRepo.getRadioScore());
                break;
            case QuType.MULTI: //多选
                examPaperQu.setActualScore(examRepo.getMultiScore());
                examPaperQu.setScore(examRepo.getMultiScore());
                break;
            case QuType.JUDGE: //判断
                examPaperQu.setActualScore(examRepo.getJudgeScore());
                examPaperQu.setScore(examRepo.getJudgeScore());
                break;
            case QuType.BLANK: //填空 有actual_score来区别两者
                examPaperQu.setActualScore(examRepo.getJudgeScore());
                examPaperQu.setScore(examRepo.getJudgeScore());
            case QuType.PROGRAM:    //编程题
                examPaperQu.setScore(examRepo.getSaqScore());
                examPaperQu.setActualScore(0);
                break;
        }

        return examPaperQu;
    }


    //康拓散列逆向
    public <T> List<T> reverseCantor(List<T> lists){
        if(lists.isEmpty())
            return null;
        int size = lists.size();
        List<T> list_cantor = new ArrayList<T>();
        //数据阶乘
        int[] f = this.factorial(size);

        int num = (int)(Math.random()*(f[size - 1]));

        //构建字典
        List<Integer> dictionary = this.dictionary(size);

        for(int i = 1 ; i <= size ; i++){
            int temp = num / f[size - i];
            num %= f[size - i];
            list_cantor.add(lists.get(dictionary.get(temp) - 1) );
            dictionary.remove(temp);
        }

        return list_cantor;
    }

    //阶乘
    private int[] factorial(int n){
        int[] arr = new int[n];

        if(n <= 1){
            arr[0] = 1;
        }else{
            arr[0] = arr[1] = 1;
        }

        if( n >= 10){
            n = 10;
        }

        for(int i = 2 ; i < n ; i++){
            arr[i] = arr[i - 1] * i;
        }

        return arr;
    }

    //构建字典
    private List<Integer> dictionary(int n){

        List dictionary_size = new ArrayList<Integer>();
        for(int i = 1 ; i <= n; i++){
            dictionary_size.add(i);
        }
        return dictionary_size;
    }
}
